/*

    Support for SkymapII / SkymapIIIC & KMD150 ascii files

    Copyright (C) 2008 Olaf Klein, o.b.klein@gpsbabel.org

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA

 */


#include "defs.h"
#include <ctype.h>
#include <time.h>
#include "strptime.h"


#define MYNAME "skyforce"


static
arglist_t skyforce_args[] = {
	ARG_TERMINATOR
};

static gbfile *fin, *fout;
static int rte_num, wpt_num;
static short_handle short_h;
static const waypoint *prev_wpt;


static waypoint *
skyforce_parse_coords(const char *str)
{
	waypoint *wpt;

	if (strlen(str) < 38) fatal(MYNAME ": Incomplete line!\n");

	wpt = waypt_new();

	wpt->latitude = atof(str + 21);
	if (str[20] == 'S') wpt->latitude = -wpt->latitude;
	wpt->latitude = ddmm2degrees(wpt->latitude);

	wpt->longitude = atof(str + 30);
	if (str[29] == 'W') wpt->longitude = -wpt->longitude;
	wpt->longitude = ddmm2degrees(wpt->longitude);

	return wpt;
}


static waypoint *
skyforce_parse_wpt(const char *str, int *rte_num)
{
	waypoint *wpt;

	wpt = skyforce_parse_coords(str);
	if (wpt == NULL) return NULL;

	wpt->shortname = lrtrim(xstrndup(str + 10, 9));

	if (rte_num) *rte_num = atoi(str + 2);

	return wpt;
}


static waypoint *
skyforce_parse_trk(const char *str)
{
	char *cx;
	struct tm tm;
	char buf[15];
	int len;

	waypoint *wpt;

	wpt = skyforce_parse_coords(str);
	if (wpt == NULL) return NULL;

	memset(&tm, 0, sizeof(tm));
	strncpy(buf, str + 2, sizeof(buf) - 1);
        buf[14] = 0;

	cx = strptime(buf, "%d%m%y  %H%M%S ", &tm);
	if ((cx != NULL) && (*cx != '\0'))
		fatal(MYNAME ": Could not parse date string (%s - %s).\n", buf, cx);

	wpt->creation_time = mkgmtime(&tm);

	len = strlen(str);

	if (len >= 45) WAYPT_SET(wpt, speed, KNOTS_TO_MPS(atof(str + 39)));
	if (len >= 59) {
		wpt->altitude = FEET_TO_METERS(atof(str + 54));
		if (str[53] == '-') wpt->altitude = -wpt->altitude;
	}

	return wpt;
}


static void
skyforce_head_disp_cb(const route_head *head)
{
	prev_wpt = NULL;
	if (head->rte_waypt_ct <= 0) return;

	wpt_num = 0;
	rte_num++;

	if (rte_num > 999) {
		if (rte_num == 1000) warning(MYNAME ": Can't store more than 999 routes. Some routes skipped!\n");
		return;
	}
}


static void
skyforce_waypt_disp_cb(const waypoint *wpt)
{
	char buf[75];	/* long enough for all data types */
	double lat, lon;


	memset(buf, ' ', sizeof(buf));
	buf[sizeof(buf) - 1] = '\0';

	switch(global_opts.objective) {
		case wptdata: buf[0] = 'W'; break;
		case trkdata: buf[0] = 'L'; break;
		case rtedata: buf[0] = 'R'; break;
		default: ; /* should never happen */
	}

	if (global_opts.objective == trkdata) {
		struct tm tm;

		tm = *gmtime(&wpt->creation_time);
		strftime(buf + 2, sizeof(buf) - 2, "%d%m%y  %H%M%S    ", &tm);
	}
	else {
		char *name;

		if (rte_num > 999) return;

		wpt_num++;
		if (wpt_num > 999) {
			if (wpt_num == 1000)
				warning(MYNAME ": Can't store more than 999 waypoints. Some waypoints skipped!\n");
			return;
		}
		if (global_opts.synthesize_shortnames)
			name = mkshort_from_wpt(short_h, wpt);
		else
			name = mkshort(short_h, wpt->shortname);

		if (global_opts.objective == rtedata)
			snprintf(buf + 2, sizeof(buf) - 2, "%03d ", rte_num);
		snprintf(buf + 6, sizeof(buf) - 6, "%03d %-9s ", wpt_num, name);
	}


	lat = degrees2ddmm(wpt->latitude);
	buf[20] = (wpt->latitude < 0) ? 'S' : 'N';
	snprintf(&buf[21], sizeof(buf) - 21, "%06.2f ", fabs(lat));

	lon = degrees2ddmm(wpt->longitude);
	buf[29] = (wpt->longitude < 0) ? 'W' : 'E';
	snprintf(&buf[30], sizeof(buf) - 30, "%08.2f ", fabs(lon));

	if (global_opts.objective == trkdata) {
		double alt, speed;

		if (wpt->altitude == unknown_alt) alt = 0;
		else alt = METERS_TO_FEET(wpt->altitude);
		speed = MPS_TO_KNOTS(waypt_speed(prev_wpt, wpt));

		snprintf(&buf[39], sizeof(buf) - 39, "%06.2f 000.00 %c%05d",
			speed,
			alt < 0 ? '-' : '+', si_round(fabs(alt)));
	}

	rtrim(buf);
	gbfprintf(fout, "%s\n", buf);

	prev_wpt = wpt;
}

/*******************************************************************************
* %%%        global callbacks called by gpsbabel main process              %%% *
*******************************************************************************/

static void
skyforce_rd_init(const char *fname)
{
	fin = gbfopen(fname, "r", MYNAME);
}


static void
skyforce_rd_deinit(void)
{
	gbfclose(fin);
}


static void
skyforce_read(void)
{
	char *str;
	route_head *rte, *trk;

	wpt_num = 0;
	rte = trk = NULL;
	rte_num = -1;

	while ((str = gbfgetstr(fin))) {

		waypoint *wpt;
		int i;

		str = lrtrim(str);
		if (*str == '\0') continue;

		switch(*str) {

			case 'W':
				wpt = skyforce_parse_wpt(str, NULL);
				if (wpt == NULL) continue;
				waypt_add(wpt);
				break;

			case 'R':
				wpt = skyforce_parse_wpt(str, &i);
				if (wpt == NULL) continue;

				if (i != rte_num) {
					rte_num = i;
					rte = NULL;
				}

				if (rte == NULL) {
					rte = route_head_alloc();
					route_add_head(rte);
					rte->rte_num = rte_num;
				}
				route_add_wpt(rte, wpt);
				break;

			case 'L':
				wpt = skyforce_parse_trk(str);
				if (wpt == NULL) continue;
				if (trk == NULL) {
					trk = route_head_alloc();
					track_add_head(trk);
				}
				track_add_wpt(trk, wpt);
				break;

			default:
				fatal(MYNAME ": Invalid line marker '%c'!\n", *str);
		}
	}
}


static void
skyforce_wr_init(const char *fname)
{
	fout = gbfopen(fname, "w", MYNAME);

	short_h = mkshort_new_handle();

	setshort_length(short_h, 9);
	setshort_badchars(short_h, "\r\n\t");
	setshort_mustupper(short_h, 1);
	setshort_mustuniq(short_h, 1);
	setshort_whitespace_ok(short_h, 0);
	setshort_repeating_whitespace_ok(short_h, 0);

	wpt_num = 0;
	rte_num = 0;
}


static void
skyforce_wr_deinit(void)
{
	mkshort_del_handle(&short_h);
	gbfclose(fout);
}


static void
skyforce_write(void)
{
	switch(global_opts.objective) {	/* We can only write one data type at a time */

		case wptdata:
			setshort_defname(short_h, "WPT");
			waypt_disp_all(skyforce_waypt_disp_cb);
			break;

		case rtedata:
			setshort_defname(short_h, "RTE");
			setshort_mustuniq(short_h, 0);
			route_disp_all(skyforce_head_disp_cb, NULL, skyforce_waypt_disp_cb);
			break;

		case trkdata:
			track_disp_all(skyforce_head_disp_cb, NULL, skyforce_waypt_disp_cb);
			break;

		case posndata:
			fatal(MYNAME ": Realtime positioning not supported.\n");
			break;

		default:
			fatal(MYNAME ": Unknown data mode!\n");
	}
}


/**************************************************************************/

ff_vecs_t skyforce_vecs = {
	ff_type_file,
	FF_CAP_RW_ALL,	/* read and write waypoints, tracks and routes*/
	skyforce_rd_init,
	skyforce_wr_init,
	skyforce_rd_deinit,
	skyforce_wr_deinit,
	skyforce_read,
	skyforce_write,
	NULL,
	skyforce_args,
	CET_CHARSET_ASCII, 1
};

/**************************************************************************/
